package server.entity;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.synyx.hades.dao.GenericDao;

import server.factory.MockFactory;
import fr.appliClient.server.entity.AbstractEntity;
import fr.appliClient.server.entity.Vehicle;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/test-context.xml", "classpath:spring/test-dao-context.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public abstract class CRUD<E extends AbstractEntity<PK>, PK extends Serializable, I extends GenericDao<E, PK>>
		implements ICRUD<PK, E, I> {

	@PersistenceContext
	protected EntityManager entityManager;

	protected Class<E> entityClass;

	protected I dao;

	@SuppressWarnings("unchecked")
	public CRUD() {

		this.entityClass = (Class<E>) (((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
	}

	@Test
	public void create() {

		E entity = MockFactory.on(this.entityClass).create();
		if (entity instanceof Vehicle) {
			this.entityManager.persist(entity);
		} else {
			this.dao.save(entity);
		}
		Assert.assertTrue(this.entityManager.contains(entity));
	}

	@Test
	public void read() {

		E entity = MockFactory.on(this.entityClass).createAndPersist(this.entityManager);
		E entity2 = this.dao.readByPrimaryKey(entity.getId());
		Assert.assertEquals(entity, entity2);
	}

	@Test
	public void update() throws IllegalArgumentException, IllegalAccessException {

		E entity = MockFactory.on(this.entityClass).createAndPersist(this.entityManager);
		changeEntityProperties(entity);
		E entity2 = this.dao.readByPrimaryKey(entity.getId());
		Assert.assertEquals(entity, entity2);
	}

	@Test
	public void delete() {

		E entity = MockFactory.on(this.entityClass).createAndPersist(this.entityManager);
		this.dao.delete(entity);
		Assert.assertTrue(!this.entityManager.contains(entity));
	}

	@Autowired
	public void config(I dao) {

		this.dao = dao;
	}

	@SuppressWarnings("boxing")
	private void changeEntityProperties(E entity) throws IllegalAccessException {

		for (Field field : this.entityClass.getFields()) {
			if (field.getName() != "id") {

				if (field.getType().equals(String.class)) {
					String entry = (String) field.get(entity);
					field.set(entity, entry + "changed");

				} else if (field.getType().equals(boolean.class)) {
					field.set(entity, !field.getBoolean(entity));

				} else if (field.getType().equals(short.class)) {
					field.set(entity, field.getShort(entity) + 1);

				} else if (field.getType().equals(int.class)) {
					field.set(entity, field.getInt(entity) + 1);

				} else if (field.getType().equals(long.class)) {
					field.set(entity, field.getLong(entity) + 1);

				} else if (field.getType().equals(float.class)) {
					field.set(entity, field.getFloat(entity) + 1);

				} else if (field.getType().equals(double.class)) {
					field.set(entity, field.getDouble(entity) + 1);
				}
			}
		}
	}

}
